home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Linux Cubed Series 7: Sunsite
/
Linux Cubed Series 7 - Sunsite Vol 1.iso
/
system
/
filesyst
/
dosfs
/
dmsdosfs.000
/
dmsdosfs
/
dmsdosfs-0.6.9b
/
dmsdos_alloc.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-07-29
|
16KB
|
557 lines
/*
linux/fs/dmsdos/dmsdos_alloc.c
DMSDOS filesystem: cluster allocation functions
******************************************************************************
DMSDOS (Doublespace/Drivespace compressed MSDOS filesystem) for Linux
written 1995,1996 by Frank Gockel
(C) Copyright 1995,1996 by Frank Gockel
Some code of the dmsdos filesystem has been copied from the msdos filesystem
so there are the following additional copyrights:
(C) Copyright 1992,1993 by Werner Almesberger (msdos filesystem)
(C) Copyright 1994,1995 by Jacques Gelinas (mmap code)
(C) Copyright 1992-1995 by Linus Torvalds
The DMSDOS filesystem was inspired by the THS filesystem (a simple doublespace
DS-0-2 compressed read-only filesystem) written 1994 by Thomas Scheuermann.
The DMSDOS filesystem is distributed under the Gnu General Public Licence.
See file COPYING for details.
******************************************************************************
*/
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/msdos_fs.h>
#include <linux/dmsdos_fs.h>
#include <linux/string.h>
#include <linux/sched.h>
extern Dblsb dblsb[];
#define NEAR_AREA 512
/* no change for doublespace, but fixes drivespace 3 problems (was 50) */
#define BIG_HOLE (dblsb[cvfnr].s_sectperclust*3+2)
struct wait_queue * alloc_wait = NULL;
int alloc_lock=0;
void lock_alloc(void)
{ while(alloc_lock)sleep_on(&alloc_wait);
alloc_lock=1;
}
void unlock_alloc(void)
{ alloc_lock=0;
wake_up(&alloc_wait);
}
struct wait_queue * readwrite_wait = NULL;
int readwrite_lock=0;
void lock_readwrite(void)
{ while(readwrite_lock)sleep_on(&readwrite_wait);
readwrite_lock=1;
}
void unlock_readwrite(void)
{ readwrite_lock=0;
wake_up(&readwrite_wait);
}
void free_chain(struct super_block*sb, int clusternr, int cvfnr)
{
Mdfat_entry mde,dummy,newmde;
int newval=0;
int i;
int sektors;
int sektornr;
int merk;
lock_alloc();
while(clusternr>0)
{ /*printk("DMSDOS: free chain: freeing cluster %d\n",clusternr);*/
/* read mdfat entry and clear */
newmde.sector_minus_1=0;
newmde.size_lo_minus_1=0;
newmde.size_hi_minus_1=0;
newmde.flags=0;
dbl_mdfat_value(sb,clusternr,cvfnr,NULL,&mde);
dbl_mdfat_value(sb,clusternr,cvfnr,&newmde,&dummy);
sektors=mde.size_lo_minus_1+1;
sektornr=mde.sector_minus_1+1;
if(mde.flags&2)
{
/* free sectors in bitfat */
for(i=0;i<sektors;++i)dbl_bitfat_value(sb,sektornr+i,cvfnr,&newval);
dblsb[cvfnr].s_full=0;
}
else
{ printk("DMSDOS: CVF %d: invalid MDFAT entry for cluster %d, zeroing it - check filesystem\n",
cvfnr+1,clusternr);
}
/* read fat entry and clear */
merk=clusternr;
clusternr=dbl_fat_nextcluster(sb,clusternr,cvfnr,NULL);
dbl_fat_nextcluster(sb,merk,cvfnr,&newval);
}
unlock_alloc();
}
int find_free_bitfat(struct super_block*sb, int sektornr,int cvfnr, int size)
{ int testsek;
int i;
if(sektornr>=dblsb[cvfnr].s_datastart&&sektornr<=dblsb[cvfnr].s_dataend-size)
{ /* search exactly fitting hole near sektornr */
testsek=sektornr;
while(testsek<sektornr+NEAR_AREA)
{ if(dbl_bitfat_value(sb,testsek,cvfnr,NULL))
{ ++testsek;
continue;
}
i=1;
while(i<=size&&dbl_bitfat_value(sb,testsek+i,cvfnr,NULL)==0)++i;
if(i==size)
{ dblsb[cvfnr].s_full=0;
return testsek;
}
testsek+=i;
}
testsek=sektornr;
while(testsek>sektornr-NEAR_AREA)
{ if(dbl_bitfat_value(sb,testsek,cvfnr,NULL))
{ --testsek;
continue;
}
i=1;
while(i<=size&&dbl_bitfat_value(sb,testsek-i,cvfnr,NULL)==0)++i;
if(i==size)
{ dblsb[cvfnr].s_full=0;
return testsek-i+1;
}
testsek-=i;
}
}
/* search for a big hole */
testsek=dblsb[cvfnr].s_datastart;
while(testsek<=dblsb[cvfnr].s_dataend-size)
{ if(dbl_bitfat_value(sb,testsek,cvfnr,NULL))
{ ++testsek;
continue;
}
i=1;
while(i<BIG_HOLE&&dbl_bitfat_value(sb,testsek+i,cvfnr,NULL)==0)++i;
if(i==BIG_HOLE)
{ dblsb[cvfnr].s_full=0;
return testsek;
}
testsek+=i;
}
/* search for an exactly fitting hole */
testsek=dblsb[cvfnr].s_datastart;
while(testsek<=dblsb[cvfnr].s_dataend-size)
{ if(dbl_bitfat_value(sb,testsek,cvfnr,NULL))
{ ++testsek;
continue;
}
i=1;
while(i<=size&&dbl_bitfat_value(sb,testsek+i,cvfnr,NULL)==0)++i;
if(i==size)
{ dblsb[cvfnr].s_full=0;
return testsek;
}
testsek+=i;
}
if(dblsb[cvfnr].s_full==0)
printk(KERN_EMERG "DMSDOS: CVF %d almost full or highly fragmented at MDFAT level, refusing further non-root write access.\n",
cvfnr+1);
dblsb[cvfnr].s_full=1;
/* last trial: search for any hole >= size */
testsek=dblsb[cvfnr].s_datastart;
while(testsek<=dblsb[cvfnr].s_dataend-size)
{ if(dbl_bitfat_value(sb,testsek,cvfnr,NULL))
{ ++testsek;
continue;
}
i=1;
while(i<size&&dbl_bitfat_value(sb,testsek+i,cvfnr,NULL)==0)++i;
if(i==size)return testsek;
testsek+=i;
}
/* not found, means disk full or MDFAT too fragmented */
dblsb[cvfnr].s_full=2;
printk(KERN_EMERG "DMSDOS: CVF %d full or too fragmented at MDFAT level - see doc\n",cvfnr+1);
return 0;
}
/* finds a free cluster, marks it as end in fat,
returns clusternr
returns -ENOSPC if disk full
**** do not call directly, call the user preferable allocation functions
instead
*/
int allocate_cluster(struct super_block*sb, int cvfnr)
{
int i;
int newval;
int clusternr;
Mdfat_entry mde;
/* when filesystem is almost full, only root may use the last BIG_HOLE
sectors since it can be dangerous to fill a CVF upto its neck
- so ignore the s_full flag on root */
if(dblsb[cvfnr].s_full!=0&¤t->euid!=0)return -ENOSPC;
/* if there's *really* nothing free any more, don't let root write */
if(dblsb[cvfnr].s_full==2)return -ENOSPC;
/* find free cluster in fat */
clusternr=0;
for(i=2;i<=dblsb[cvfnr].s_max_cluster;++i)
{ if(dbl_fat_nextcluster(sb,i,cvfnr,NULL)==0)
{ clusternr=i;
/* check mdfat if empty */
dbl_mdfat_value(sb,clusternr,cvfnr,NULL,&mde);
if(mde.flags&2)
{ printk("DMSDOS: CVF %d: MDFAT contradicts FAT for cluster %d, ignoring entry - check filesystem!\n",
cvfnr+1,clusternr);
}
else
{
/* okay, mark appended cluster as 'end' */
newval=0xFFFF;
dbl_fat_nextcluster(sb,clusternr,cvfnr,&newval);
return clusternr;
}
}
}
return -ENOSPC;
}
/* user preferable cluster allocation routines */
int allocate_first_cluster(struct super_block*sb, int cvfnr)
{ int res;
lock_alloc();
res=allocate_cluster(sb,cvfnr);
unlock_alloc();
return res;
}
int allocate_next_cluster(struct super_block*sb, int akt_cluster, int cvfnr)
{
int res;
lock_alloc();
res=allocate_cluster(sb,cvfnr);
if(res>0)dbl_fat_nextcluster(sb,akt_cluster,cvfnr,&res);
unlock_alloc();
return res;
}
/* replaces an existing cluster;
this unusual function must be called before rewriting any file cluster;
*** size must be known (encoded in mde) ***
it does nothing if called too often;
returns first sector nr
*/
int replace_existing_cluster(struct super_block*sb, int cluster,
int near_sector,
Mdfat_entry*mde, int cvfnr)
{ Mdfat_entry old_mde,new_mde,dummy;
int i;
int newval;
int sektor;
int old_sektor;
int old_size;
int new_size;
lock_alloc();
/* printk("DMSDOS: replace_existing_cluster cluster=%d near_sector=%d mdfatbits=0x%x cvfnr=%d\n",
cluster,near_sector,mdfatbits,cvfnr+1);*/
dbl_mdfat_value(sb,cluster,cvfnr,NULL,&old_mde);
old_size=old_mde.size_lo_minus_1+1;
old_sektor=old_mde.sector_minus_1+1;
new_size=mde->size_lo_minus_1+1;
if(old_mde.flags&2)
{
/* test whether same length */
if(old_size==new_size)
{ /*printk("DMSDOS: replace_existing_cluster: same length, ok\n");*/
sektor=old_sektor;
goto mdfat_update;
}
/* different length, replace mdfat entry */
newval=0;
/*printk("DMSDOS: replace_existing_cluster: freeing old sectors...\n");*/
for(i=0;i<old_size;++i)dbl_bitfat_value(sb,old_sektor+i,cvfnr,&newval);
/*printk("DMSDOS: replace_existing_cluster: freeing finished\n");*/
}
/*printk("DMSDOS: replace_existing_cluster: call find_free_bitfat...\n");*/
sektor=find_free_bitfat(sb,near_sector,cvfnr,new_size);
/*printk("DMSDOS: replace_existing_cluster: find_free_bitfat returned %d\n",
sektor);*/
if(sektor==0)
{ if(old_mde.flags&2)
{
/* undo bitfat free */
newval=1;
for(i=0;i<old_size;++i)dbl_bitfat_value(sb,old_sektor+i,cvfnr,&newval);
}
unlock_alloc();
return -ENOSPC; /* disk full */
}
/* check whether really free (bug supposed in find_free_bitfat) */
for(i=0;i<new_size;++i)
{ if(dbl_bitfat_value(sb,sektor+i,cvfnr,NULL))
{ printk(KERN_EMERG "DMSDOS: find_free_bitfat returned sektor %d size %d but they are not all free!\n",
sektor,new_size);
unlock_alloc();
panic("This is a dmsdos bug - reboot and check filesystem\n");
return -EIO;
}
}
newval=1;
/*printk("DMSDOS: replace_existing_cluster: allocating in bitfat...\n");*/
for(i=0;i<new_size;++i)dbl_bitfat_value(sb,sektor+i,cvfnr,&newval);
mdfat_update:
new_mde.sector_minus_1=sektor-1;
new_mde.size_lo_minus_1=mde->size_lo_minus_1;
new_mde.size_hi_minus_1=mde->size_hi_minus_1;
new_mde.flags=mde->flags|2;
/*printk("DMSDOS: replace_existing_cluster: writing mdfat...\n");*/
dbl_mdfat_value(sb,cluster,cvfnr,&new_mde,&dummy);
unlock_alloc();
return sektor; /* okay */
}
/* user preferable cluster allocation functions for directories */
/* cluster is uncompressed, of maximum size and zerod out and written */
int allocate_first_dir_cluster(struct super_block*sb,int cvfnr)
{ int res;
int sektor;
int cluster;
int i;
int size;
struct buffer_head*bh;
int newval;
Mdfat_entry mde;
res=allocate_first_cluster(sb,cvfnr);
if(res<=0)return res;
cluster=res;
size=dblsb[cvfnr].s_sectperclust;
mde.size_lo_minus_1=size-1;
mde.size_hi_minus_1=size-1;
mde.flags=3;
res=replace_existing_cluster(sb,cluster,0,&mde,cvfnr);
if(res<0)
{ /* undo cluster allocation */
newval=0;
dbl_fat_nextcluster(sb,cluster,cvfnr,&newval);
return res;
}
/* zero it out since it is an empty dir cluster */
sektor=res;
for(i=0;i<size;++i)
{ bh=noread_dbl_sector(sb,sektor+i,cvfnr);
if(bh==NULL)
{ /* undo cluster allocation */
newval=0;
dbl_fat_nextcluster(sb,cluster,cvfnr,&newval);
return -EIO;
}
memset(bh->b_data,0,SECTOR_SIZE);
bh_dirty(sb,bh);
bh_free(sb,bh);
}
return cluster;
}
int allocate_next_dir_cluster(struct super_block*sb, int akt_cluster,
int cvfnr)
{ int res;
int sektor;
int i;
struct buffer_head*bh;
int cluster;
int size;
int newval;
Mdfat_entry mde;
res=allocate_next_cluster(sb,akt_cluster,cvfnr);
if(res<=0)return res;
cluster=res;
size=dblsb[cvfnr].s_sectperclust;
mde.size_lo_minus_1=size-1;
mde.size_hi_minus_1=size-1;
mde.flags=3;
sektor=dbl_mdfat_cluster2sector(sb,akt_cluster,cvfnr);
res=replace_existing_cluster(sb,cluster,sektor,&mde,cvfnr);
if(res<0)
{ /* undo cluster allocation */
newval=0;
dbl_fat_nextcluster(sb,cluster,cvfnr,&newval);
newval=-1;
dbl_fat_nextcluster(sb,akt_cluster,cvfnr,&newval);
return res;
}
/* zero it out since it is an empty dir cluster */
sektor=res;
for(i=0;i<size;++i)
{ bh=noread_dbl_sector(sb,sektor+i,cvfnr);
if(bh==NULL)
{ /* undo cluster allocation */
newval=0;
dbl_fat_nextcluster(sb,cluster,cvfnr,&newval);
newval=-1; /* cut off or directory will be unreadable */
dbl_fat_nextcluster(sb,akt_cluster,cvfnr,&newval);
return -EIO;
}
memset(bh->b_data,0,SECTOR_SIZE);
bh_dirty(sb,bh);
bh_free(sb,bh);
}
return cluster;
}
int scan_dbl_dir_4_empty(struct super_block*sb, int dirstartclust, int cvfnr)
{ return scan_dbl_dir_4_n_empty(sb,dirstartclust,cvfnr,1,NULL);
}
/* find n empty dir entries
append a dir cluster if necessary
fills lfn_inos if !=NULL
*/
int scan_dbl_dir_4_n_empty(struct super_block*sb, int dirstartclust, int cvfnr,
int n,int*lfn_inos)
{ int ino;
int ent=0;
unsigned char buf[32];
int lastcluster;
int merk;
struct inode * inode;
int found=0;
/* fill first unused with zero -- for safety */
if(lfn_inos)lfn_inos[n]=0;
while((ino=read_dbl_direntry(sb,dirstartclust,cvfnr,ent,buf))>0)
{ ++ent;
if(buf[0]==0xe5||buf[0]==0)
{ inode=iget(sb,ino);
if(MSDOS_I(inode)->i_busy==0)
{ iput(inode);
++found;
if(lfn_inos)lfn_inos[n-found]=ino;
if(n==found)return ino;
}
else
{ found=0;
iput(inode);
}
}
else found=0;
}
if(dirstartclust==0)return -ENOSPC; /* root dir full */
if(ent%(dblsb[cvfnr].s_sectperclust*16))
{ printk("DMSDOS: scan_dbl_dir_4_empty: scan error occured, refusing to append cluster\n");
return -ENOSPC;
}
/* okay, must append a cluster */
lastcluster=dirstartclust;
while((merk=dbl_fat_nextcluster(sb,lastcluster,cvfnr,NULL))>0)
lastcluster=merk;
lastcluster=allocate_next_dir_cluster(sb,lastcluster,cvfnr);
if(lastcluster<0) return -ENOSPC; /* disk full */
/* the cluster is already zerod out */
while(found<n)
{
ino=read_dbl_direntry(sb,dirstartclust,cvfnr,ent,buf);
if(ino<0)
{ printk("DMSDOS: scan_dbl_dir_4_empty: error after appending dir cluster\n");
return ino;
}
++found;++ent;
if(lfn_inos)lfn_inos[n-found]=ino;
}
return ino;
}
int moveback(struct super_block*sb, int clusternr, int cvfnr)
{ Mdfat_entry mde,dummy;
int sector;
int i,newval;
struct buffer_head*bh1,*bh2;
if(clusternr<2||clusternr>dblsb[cvfnr].s_max_cluster)return -EINVAL;
lock_alloc();
dbl_mdfat_value(sb,clusternr,cvfnr,NULL,&mde);
if(mde.flags&2) /* used */
{ sector=mde.sector_minus_1;
if(dbl_bitfat_value(sb,sector,cvfnr,NULL)==0)
{ while(dbl_bitfat_value(sb,sector-1,cvfnr,NULL)==0)--sector;
/* copy cluster */
/* printk("DMSDOS: moving cluster %d from sector %ld to %d\n",clusternr,
mde.sector_minus_1+1,sector);*/
for(i=0;i<=mde.size_lo_minus_1;++i)
{ bh1=read_dbl_sector(sb,mde.sector_minus_1+1+i,cvfnr);
if(bh1==NULL) goto err_out;
bh2=noread_dbl_sector(sb,sector+i,cvfnr);
if(bh2==NULL)
{ bh_free(sb,bh1);
err_out:
unlock_alloc();
printk("DMSDOS: moveback failed (couldn't read data)!\n");
return -EIO;
}
memcpy(bh2->b_data,bh1->b_data,SECTOR_SIZE);
bh_dirty(sb,bh2);
bh_free(sb,bh1);
bh_free(sb,bh2);
}
/* free in bitfat */
newval=0;
for(i=0;i<=mde.size_lo_minus_1;++i)
dbl_bitfat_value(sb,mde.sector_minus_1+1+i,cvfnr,&newval);
/* allocate in bitfat */
newval=1;
for(i=0;i<=mde.size_lo_minus_1;++i)
dbl_bitfat_value(sb,sector+i,cvfnr,&newval);
/* save in mdfat */
mde.sector_minus_1=sector-1;
dbl_mdfat_value(sb,clusternr,cvfnr,&mde,&dummy);
}
}
unlock_alloc();
return 0;
}